

#include "formationscene.h"
#include "resourcemanager.h"
#include "log.h"
#include "messagequeue.h"
#include "gamemanager.h"
#include "gamedatamanager.h"
#include "unitscene.h"
#include "unit.h"
#include "CCSkeletalSprite.h"
#include "unitfactory.h"
#include "unitdetaillayer.h"
#include "armiesscene.h"

static const float SHOW_UNIT_BAR_THRESHOLD = 10.0f;

static const int EDIT_GRID_WIDTH = 70;
static const int EDIT_GRID_HEIGHT = 70;

static const int EDIT_SOLDIER_BOTTOM_MARGIN = 10;

static const int EDIT_SQUAD_SPACE = 50;
static const int EDIT_SQUAD_WIDTH = EDIT_GRID_WIDTH * LINES_PER_SQUAD;
static const int EDIT_SQUAD_HEIGHT = EDIT_GRID_HEIGHT * ROWS_PER_LINE;

static const int FORMATION_VIEW_HEADER_HEIGHT = 30;
static const int FORMATION_VIEW_FOOTER_HEIGHT = 40;
static const int FORMATION_VIEW_MARGIN = 5;
static const int FORMATION_VIEW_WIDTH = (EDIT_SQUAD_WIDTH + EDIT_SQUAD_SPACE) * SQUAD_COUNT - EDIT_SQUAD_SPACE;
static const int FORMATION_VIEW_HEIGHT = EDIT_SQUAD_HEIGHT + FORMATION_VIEW_FOOTER_HEIGHT + FORMATION_VIEW_FOOTER_HEIGHT;

static const int UNIT_BAR_ITEM_WIDTH = 80;
static const int UNIT_BAR_ITEM_HEIGHT = 80;
static const int UNIT_BAR_ITEM_SPACE = 10;

static const char *TOUCH_PLACE_NAMES[] = {
		"blank",
		"formation_view",
		"equipment_detail_view",
		"soldier",
		"unit_bar",
		"unit",
		"create_unit",
		"addUnit",
		"slide_formation_view",
		"show_unit_bar",
};

static const int LINES_X_POS[] = {
		FORMATION_VIEW_MARGIN + EDIT_GRID_WIDTH / 2 + EDIT_GRID_WIDTH * 14 + EDIT_SQUAD_SPACE * 4,
		FORMATION_VIEW_MARGIN + EDIT_GRID_WIDTH / 2 + EDIT_GRID_WIDTH * 13 + EDIT_SQUAD_SPACE * 4,
		FORMATION_VIEW_MARGIN + EDIT_GRID_WIDTH / 2 + EDIT_GRID_WIDTH * 12 + EDIT_SQUAD_SPACE * 4,

		FORMATION_VIEW_MARGIN + EDIT_GRID_WIDTH / 2 + EDIT_GRID_WIDTH * 11 + EDIT_SQUAD_SPACE * 3,
		FORMATION_VIEW_MARGIN + EDIT_GRID_WIDTH / 2 + EDIT_GRID_WIDTH * 10 + EDIT_SQUAD_SPACE * 3,
		FORMATION_VIEW_MARGIN + EDIT_GRID_WIDTH / 2 + EDIT_GRID_WIDTH * 9 + EDIT_SQUAD_SPACE * 3,

		FORMATION_VIEW_MARGIN + EDIT_GRID_WIDTH / 2 + EDIT_GRID_WIDTH * 8 + EDIT_SQUAD_SPACE * 2,
		FORMATION_VIEW_MARGIN + EDIT_GRID_WIDTH / 2 + EDIT_GRID_WIDTH * 7 + EDIT_SQUAD_SPACE * 2,
		FORMATION_VIEW_MARGIN + EDIT_GRID_WIDTH / 2 + EDIT_GRID_WIDTH * 6 + EDIT_SQUAD_SPACE * 2,

		FORMATION_VIEW_MARGIN + EDIT_GRID_WIDTH / 2 + EDIT_GRID_WIDTH * 5 + EDIT_SQUAD_SPACE,
		FORMATION_VIEW_MARGIN + EDIT_GRID_WIDTH / 2 + EDIT_GRID_WIDTH * 4 + EDIT_SQUAD_SPACE,
		FORMATION_VIEW_MARGIN + EDIT_GRID_WIDTH / 2 + EDIT_GRID_WIDTH * 3 + EDIT_SQUAD_SPACE,

		FORMATION_VIEW_MARGIN + EDIT_GRID_WIDTH / 2 + EDIT_GRID_WIDTH * 2,
		FORMATION_VIEW_MARGIN + EDIT_GRID_WIDTH / 2 + EDIT_GRID_WIDTH,
		FORMATION_VIEW_MARGIN + EDIT_GRID_WIDTH / 2,
};

FormationScene::FormationScene():
_layer(nullptr)
, _layerFormation(nullptr)
, _unitDetailLayer(nullptr)
, formationIndex(0)
, _draggingSoldier(nullptr)
, _addingUnit(nullptr)
, _garbageZone(nullptr)
, _draggingStartGrid(-1)
, _draggingTargetGrid(-1)
, _selectedUnitBarItemIndex(-1)
, _isShownUnitBar(false) {

	memset(_gridsStateIndicator, 0, sizeof(_gridsStateIndicator));
	memset(_soldiers, 0, sizeof(_soldiers));
	memset(_gridsState, 0, sizeof(_gridsState));
}

FormationScene::~FormationScene() {
	for (auto item : _arrUnitBarItems) {
		delete item;
	}

	if (_addingUnit) {
		delete _addingUnit;
	}
}

FormationScene *FormationScene::create(int index) {
	auto scene = new FormationScene();
	if (scene) {
		if (scene->init(index)) {
			scene->autorelease();
		} else {
			delete scene;
			scene = nullptr;
		}
	}

	return scene;
}

bool FormationScene::init(int index) {
	if (!CCScene::init()) {
		return false;
	}

	_layer = CCLayer::create();
	addChild(_layer);

#if (CC_TARGET_PLATFORM == CC_PLATFORM_IOS)
	CCSprite *spr = CCSprite::create("textures/ui_bg.png");
#else
	CCSprite *spr = CCSprite::create("textures/ui_bg.png");
#endif

	spr->setAnchorPoint(CCPointZero);

	CCSize size = spr->getContentSize();
	spr->setScaleX(GM->screenSize.width / size.width);
	spr->setScaleY(GM->screenSize.height / size.height);
	_layer->addChild(spr);

	formationIndex = index;
	formationInfo = *(GDM->formations[formationIndex]);

	createLeftBarUIThings();
	createFormationUIThings();
	createUnitBarUIThings();

	createFormation();

	_unitDetailLayer = UnitDetailLayer::create();
	_unitDetailLayer->setVisible(false);
	addChild(_unitDetailLayer);

	scheduleUpdate();

	return true;
}

void FormationScene::createUnitBarUIThings() {
	CCSize size = CCSizeMake(GM->screenSize.width, X_SCALE(120));

	_unitBar = cocos2d::extension::CCScale9Sprite::create("textures/pad_bg.png", CCRectMake(0, 0, 225, 225), CCRectMake(100, 100, 25, 25));
	_unitBar->setContentSize(size);
	_unitBar->setPosition(ccp(GM->screenSize.width / 2.0f, 0.0f));
	_unitBar->setAnchorPoint(ccp(0.5f, 1.0f));
	_unitBar->setZOrder(200);
	_layer->addChild(_unitBar);

	_sprCreateUnit = MAKE_SPRITE("create_unit_0.png");

	float x = UNIT_BAR_ITEM_SPACE + UNIT_BAR_ITEM_WIDTH / 2;
	float y = (_unitBar->getContentSize().height - UNIT_BAR_ITEM_HEIGHT) / 2;

	_sprCreateUnit->setPosition(ccp(x, y));
	_sprCreateUnit->setAnchorPoint(ccp(0.5f, 0.0f));
	_unitBar->addChild(_sprCreateUnit);

	updateUnitBarItems();
}

void FormationScene::updateUnitBarItems() {
	for (auto item : _arrUnitBarItems) {
		delete item;
	}
	_arrUnitBarItems.clear();

	float x = X_SCALE((UNIT_BAR_ITEM_SPACE + UNIT_BAR_ITEM_WIDTH) * 2 - UNIT_BAR_ITEM_WIDTH / 2);
	float y = (_unitBar->getContentSize().height - Y_SCALE(UNIT_BAR_ITEM_HEIGHT)) / 2;

	for (UnitInfo item : formationInfo.units) {
		Unit *unit = UnitFactory::createUnit(item);
		unit->scale = getUnitScaleForUnitBar(unit);
		unit->sprite->setScale(unit->scale);
		unit->setPosition(ccp(x, y));
		_unitBar->addChild(unit->sprite);
		_arrUnitBarItems.push_back(unit);

		CCSprite *sprQuantityPad = MAKE_SPRITE("quantity_pad.png");
		sprQuantityPad->setScale(1.0f / 0.6f);
		sprQuantityPad->setPosition(ccp(0, 0));
		sprQuantityPad->setAnchorPoint(ccp(0.0f, 0.5f));
		unit->sprite->addChild(sprQuantityPad);

		sprintf(buf, "%d", 0);
		CCLabelTTF *labelQuantity = CCLabelTTF::create(buf, "", 12);
		labelQuantity->setPosition(ccp(sprQuantityPad->getContentSize().width / 2, sprQuantityPad->getContentSize().height / 2));
		labelQuantity->setAnchorPoint(ccp(0.5f, 0.5f));
		sprQuantityPad->addChild(labelQuantity);

		x += X_SCALE(UNIT_BAR_ITEM_SPACE + UNIT_BAR_ITEM_WIDTH);
	}
}

void FormationScene::createLeftBarUIThings() {
	_leftBar = MAKE_SPRITE("right_bar_bg.png");
	_leftBar->setAnchorPoint(ccp(0.0f, 0.0f));
	_leftBar->setScaleX(GM->xscale);
	_leftBar->setScaleY(GM->yscale);
	_leftBar->setZOrder(100);

	_layer->addChild(_leftBar);

	_btnSave = RM->makeButton("button/red_button_0.png", "button/red_button_1.png", nullptr, this, menu_selector(FormationScene::onSave), _leftBar);
	_btnSave->setPosition(ccp(75, 250));
	_btnSave->setScaleX(1 / GM->xscale);
	_btnSave->setScaleY(1 / GM->yscale);

	RM->makeButtonLabel("Save", 20, _btnSave);

	_btnReset = RM->makeButton("button/green_button_0.png", "button/green_button_1.png", nullptr, this, menu_selector(FormationScene::onReset), _leftBar);
	_btnReset->setPosition(ccp(75, 155));
	_btnReset->setScaleX(1 / GM->xscale);
	_btnReset->setScaleY(1 / GM->yscale);

	RM->makeButtonLabel("Reset", 20, _btnReset);

	_btnExit = RM->makeBackButton(this, menu_selector(FormationScene::onBack), _leftBar, true);
	_btnExit->setPosition(ccp(50, 524));
	_btnExit->setScaleX(1 / GM->xscale);
	_btnExit->setScaleY(1 / GM->yscale);

	_garbageZone = MAKE_SPRITE("garbage_icon.png");
	_garbageZone->setPosition(ccp(_leftBar->getContentSize().width / 2.0f, 5.0f));
	_garbageZone->setAnchorPoint(ccp(0.5f, 0.0f));
	_garbageZone->setScaleX(1 / GM->xscale);
	_garbageZone->setScaleY(1 / GM->yscale);
	_garbageZone->setVisible(false);
	_leftBar->addChild(_garbageZone);
}

void FormationScene::createFormationUIThings() {
	_formationViewInitialPosition = XY_SCALE(_leftBar->getContentSize().width + FORMATION_VIEW_MARGIN, FORMATION_VIEW_MARGIN);

	_layerFormation = CCLayer::create();
	_layerFormation->setPosition(_formationViewInitialPosition);
	_layerFormation->setContentSize(CCSize(FORMATION_VIEW_WIDTH, FORMATION_VIEW_HEIGHT));
	_layerFormation->setScale(GM->minscale);
	_layerFormation->setAnchorPoint(CCPointZero);
	_layer->addChild(_layerFormation);
	_DHLOG("formation_view_width:%d, x:%0.0f, content size width:%0.0f", FORMATION_VIEW_WIDTH, _layerFormation->getPosition().x, _layerFormation->getContentSize().width);

	for (int i = 0; i < SQUAD_COUNT; i++) {
		cocos2d::extension::CCScale9Sprite *sprSquadBg = cocos2d::extension::CCScale9Sprite::create("textures/pad_bg.png", CCRectMake(0, 0, 225, 225), CCRectMake(100, 100, 25, 25));
		sprSquadBg->setContentSize(CCSizeMake(X_SCALE(240), Y_SCALE(420)));
		sprSquadBg->setPosition(ccp(LINES_X_POS[i * LINES_PER_SQUAD + 1], FORMATION_VIEW_FOOTER_HEIGHT + 210));
		_layerFormation->addChild(sprSquadBg);

		_squadsBody[i] = MAKE_SPRITE("squad_grids.png");
		_squadsBody[i]->setScale(1.0f);
		_layerFormation->addChild(_squadsBody[i]);

		_squadsBody[i]->setPosition(ccp(LINES_X_POS[i * LINES_PER_SQUAD + 1], FORMATION_VIEW_FOOTER_HEIGHT));
		_squadsBody[i]->setAnchorPoint(ccp(0.5f, 0.0f));

		_squadsHeader[i] = MAKE_SPRITE("button/button_0.png");
		_squadsHeader[i]->setPosition(ccp(EDIT_SQUAD_WIDTH / 2, EDIT_SQUAD_HEIGHT));
		_squadsHeader[i]->setAnchorPoint(ccp(0.5f, 0.0f));
		_squadsBody[i]->addChild(_squadsHeader[i]);

		CCSprite *sprFooter = MAKE_SPRITE("button/button_0.png");
		sprFooter->setPosition(ccp(EDIT_SQUAD_WIDTH / 2, 0));
		sprFooter->setAnchorPoint(ccp(0.5f, 1.0f));
		_squadsBody[i]->addChild(sprFooter);

		_squadsFooter[i] = CCLabelTTF::create(SQUAD_ORDER_NAMES[0], "", 18);
		_squadsFooter[i]->setPosition(ccp(sprFooter->getContentSize().width / 2, sprFooter->getContentSize().height / 2));
		_squadsFooter[i]->setTag(100 + i);
		sprFooter->addChild(_squadsFooter[i]);
	}
}

void FormationScene::update(float dt) {
	handleMessage();
}


void FormationScene::onSave(CCObject *sender) {
	GDM->saveFormation(formationIndex, formationInfo);

	// save preview image
	float w = FORMATION_VIEW_WIDTH * GM->minscale;
	float h = FORMATION_VIEW_HEIGHT * GM->minscale;
	CCRenderTexture *tex = CCRenderTexture::create(static_cast<int>(w / 3.0f), static_cast<int>(h / 3.0f));
	tex->setAnchorPoint(CCPointZero);

	auto pos = _layerFormation->getPosition();
	_layerFormation->setPosition(CCPointZero);
	_layerFormation->setScale(0.33f * GM->minscale);
	_leftBar->setVisible(false);
	for (int i = 0; i < SQUAD_COUNT; i++) {
		_squadsHeader[i]->setVisible(false);
		_squadsFooter[i]->getParent()->setVisible(false);
	}
	tex->begin();
	CCDirector::sharedDirector()->getRunningScene()->visit();
	tex->end();

	_layerFormation->setPosition(pos);
	_layerFormation->setScale(GM->minscale);
	_leftBar->setVisible(true);
	for (int i = 0; i < SQUAD_COUNT; i++) {
		_squadsHeader[i]->setVisible(true);
		_squadsFooter[i]->getParent()->setVisible(true);
	}

	sprintf(buf, "formation_%d_preview.jpg", formationIndex);
	tex->saveToFile(buf, kCCImageFormatJPEG);
}

void FormationScene::onReset(CCObject *sender) {
	memset(_gridsState, 0, sizeof(_gridsState));

	for (int i = 0; i < TOTAL_LINES; i++) {
		for (int j = 0; j < ROWS_PER_LINE; j++) {
			Unit *unit = _soldiers[i][j];
			delete unit;
		}
	}

	memset(_soldiers, 0, sizeof(_soldiers));

	formationInfo.clear();

	createFormation();
}

void FormationScene::onBack(CCObject *sender) {
	ArmiesScene *scene = ArmiesScene::create();
	CCDirector::sharedDirector()->replaceScene(scene);
}

void FormationScene::createUnit(CCObject *sender) {

}

void FormationScene::editUnit(CCObject *sender) {

}

void FormationScene::deleteUnit(CCObject *sender) {

}

void FormationScene::showUnitBar() {
	if (!_isShownUnitBar) {
		_unitBar->runAction(CCMoveTo::create(0.2f, ccp(GM->screenSize.width / 2.0f, _unitBar->getContentSize().height)));
		_isShownUnitBar = true;
	}
}

bool FormationScene::ccTouchBegan(CCTouch *pTouch, CCEvent *pEvent) {
	if (_unitDetailLayer->isVisible()) {
		_unitDetailLayer->setVisible(false);
	}

	_touchPlace = FormationSceneTouchPlace::blank;

	auto pt = pTouch->getLocation();
	if (_unitBar->hitTest(pt)) {
		_touchPlace = FormationSceneTouchPlace::unit_bar;

		if (_sprCreateUnit->hitTest(pt)) {
			_touchPlace = FormationSceneTouchPlace::create_unit;
			_sprCreateUnit->setDisplayFrame(MAKE_SPRITEFRAME("create_unit_1.png"));
		} else {
			float x = UNIT_BAR_ITEM_SPACE + UNIT_BAR_ITEM_WIDTH + UNIT_BAR_ITEM_SPACE;
			float y = (_unitBar->getContentSize().height - UNIT_BAR_ITEM_HEIGHT) / 2;
			CCPoint localPt = _unitBar->convertToNodeSpace(pt);
			for (size_t i = 0; i < _arrUnitBarItems.size(); i++) {
				CCRect rect = CCRect(x * GM->minscale, y * GM->minscale, UNIT_BAR_ITEM_WIDTH * GM->minscale, UNIT_BAR_ITEM_HEIGHT * GM->minscale);
				if (rect.containsPoint(localPt)) {
					_touchPlace = FormationSceneTouchPlace::unit;
					_selectedUnitBarItemIndex = i;
					break;
				}

				x += UNIT_BAR_ITEM_SPACE + UNIT_BAR_ITEM_WIDTH;
			}
		}
	} else if (_layerFormation->hitTest(pt)) {
		_touchPlace = FormationSceneTouchPlace::formation_view;

		_draggingStartGrid = getGridIndexAtPoint(pt);
		if (_draggingStartGrid.squadIndex != -1) {
			int id = _gridsState[_draggingStartGrid.globalLineIndex][_draggingStartGrid.rowIndex];
			_draggingSoldier = getSoldierByID(id);
			if (_draggingSoldier) {
				_touchPlace = FormationSceneTouchPlace::soldier;
			}
		}
	}

	_DHLOG("touch began at place:%s", TOUCH_PLACE_NAMES[(int) _touchPlace]);

	return true;
}

void FormationScene::ccTouchMoved(CCTouch *pTouch, CCEvent *pEvent) {
	auto pt = pTouch->getLocation();
	auto offset = pTouch->getDelta();

	switch (_touchPlace) {
		case FormationSceneTouchPlace::unit: {
			if (fabs(offset.x) > TOUCH_MOVE_THRESHOLD || fabs(offset.y) > TOUCH_MOVE_THRESHOLD) {
				_touchPlace = FormationSceneTouchPlace::addUnit;

				Unit *unit = _arrUnitBarItems[_selectedUnitBarItemIndex];
				if (_addingUnit) {
					_addingUnit->changeEquipment(unit->unitInfo);
				} else {
					_addingUnit = UnitFactory::createUnit(unit->unitInfo, 0.6f);
				}


				if (_layer != _addingUnit->sprite->getParent()) {
					_layer->addChild(_addingUnit->sprite);
				}
				_layer->reorderChild(_addingUnit->sprite, 100);

				hideUnitBar();
			}
			break;
		}
		case FormationSceneTouchPlace::addUnit: {
			_garbageZone->setVisible(true);

			_draggingTargetGrid = getGridIndexAtPoint(pt);

			_addingUnit->setPosition(pt);
			_addingUnit->sprite->setVisible(true);
			_addingUnit->gridInfo = _draggingTargetGrid;

			updateGridsState(_addingUnit, _draggingTargetGrid.globalLineIndex, _draggingTargetGrid.rowIndex);

			break;
		}
		case FormationSceneTouchPlace::soldier: {
			int id = _gridsState[_draggingStartGrid.globalLineIndex][_draggingStartGrid.rowIndex];
			if (id > 0) {
				removeExistedSoldier(id);
			}

			_touchPlace = FormationSceneTouchPlace::dragging_soldier;

			break;
		}
		case FormationSceneTouchPlace::dragging_soldier: {
			auto localPt = _layerFormation->convertToNodeSpace(pt);
			_draggingTargetGrid = getGridIndexAtPoint(pt);

			_draggingSoldier->setPosition(localPt);
			_draggingSoldier->sprite->getParent()->reorderChild(_draggingSoldier->sprite, 999);
			_draggingSoldier->gridInfo = _draggingTargetGrid;

			updateGridsState(_draggingSoldier, _draggingTargetGrid.globalLineIndex, _draggingTargetGrid.rowIndex);

			break;
		}
		case FormationSceneTouchPlace::formation_view: {
			if (fabs(offset.x) > fabs(offset.y)) {
				_touchPlace = FormationSceneTouchPlace::slide_formation_view;
			} else if ((pt.y - pTouch->getStartLocation().y) > Y_SCALE(SHOW_UNIT_BAR_THRESHOLD)) {
				_touchPlace = FormationSceneTouchPlace::show_unit_bar;
			} else if ((pt.y - pTouch->getStartLocation().y) < Y_SCALE(-SHOW_UNIT_BAR_THRESHOLD)) {
				_touchPlace = FormationSceneTouchPlace::hide_unit_bar;
			}
			break;
		}
		case FormationSceneTouchPlace::slide_formation_view:
			slideFormationView(offset.x);
	        break;
		default:
			break;
	}
}

void FormationScene::slideFormationView(float dx) {
	CCPoint newPt = _layerFormation->getPosition();
	newPt.x += dx;

	float barWidth = _leftBar->getContentSize().width;

	float minx = _layerFormation->getContentSize().width * GM->minscale - (GM->screenSize.width - barWidth * GM->minscale) - barWidth * GM->minscale + FORMATION_VIEW_MARGIN * GM->minscale * 2;
	if (newPt.x < -minx) {
		_DHLOG("layer:%0.0f, screen:%0.0f, right:%0.0f", _layerFormation->getContentSize().width, GM->screenSize.width, _leftBar->getContentSize().width);
		newPt.x = -minx;
	} else if (newPt.x > (FORMATION_VIEW_MARGIN + barWidth) * GM->minscale) {
		newPt.x = (FORMATION_VIEW_MARGIN + barWidth) * GM->minscale;
	}

	_layerFormation->setPosition(newPt);
}

void FormationScene::ccTouchEnded(CCTouch *pTouch, CCEvent *pEvent) {
	CCPoint pt = pTouch->getLocation();
	switch (_touchPlace) {
		case FormationSceneTouchPlace::soldier:
			break;
		case FormationSceneTouchPlace::dragging_soldier:
			handleDroppingSoldier(pt);
	        break;
		case FormationSceneTouchPlace::formation_view:
		case FormationSceneTouchPlace::blank:
			if (_isShownUnitBar) {
				hideUnitBar();
			} else {
				changeSquadOrderState(pt);
			}
	        break;
		case FormationSceneTouchPlace::show_unit_bar:
			showUnitBar();
	        break;
		case FormationSceneTouchPlace::hide_unit_bar:
			hideUnitBar();
	        break;
		case FormationSceneTouchPlace::addUnit:
			handleAddingUnit(pt);
	        showUnitBar();
	        break;
		case FormationSceneTouchPlace::unit:
			showUnitDetail();
	        break;
		case FormationSceneTouchPlace::create_unit: {
			_sprCreateUnit->setDisplayFrame(MAKE_SPRITEFRAME("create_unit_0.png"));

			auto scene = UnitScene::create();
			scene->setFormationInfo(&formationInfo);
			CCDirector::sharedDirector()->pushScene(scene);

			break;
		}
		default:
			break;
	}

	if (_touchPlace == FormationSceneTouchPlace::formation_view) {
		changeSquadOrderState(pt);
	}

	_touchPlace = FormationSceneTouchPlace::blank;
	_draggingSoldier = nullptr;
	if (_addingUnit) {
		_addingUnit->sprite->setVisible(false);
	}

	if (_draggingTargetGrid.squadIndex != -1) {
		CCSprite *spr = _gridsStateIndicator[_draggingTargetGrid.globalLineIndex][_draggingTargetGrid.rowIndex];
		spr->setDisplayFrame(MAKE_SPRITEFRAME("grid_normal.png"));
	}

	_draggingStartGrid.set(-1);
	_draggingTargetGrid.set(-1);
	_selectedUnitBarItemIndex = -1;

	_garbageZone->setVisible(false);
}

void FormationScene::changeSquadOrderState(CCPoint pt) {
	for (int i = 0; i < SQUAD_COUNT; i++) {
		if (_squadsFooter[i]->getParent()->hitTest(pt)) {
			int n = (int) formationInfo.orders[i];
			formationInfo.orders[i] = (SquadOrder) ((n + 1) % 3);
			_squadsFooter[i]->setString(SQUAD_ORDER_NAMES[(int) formationInfo.orders[i]]);

			break;
		}
	}
}

void FormationScene::handleAddingUnit(CCPoint pt) {
	if (_garbageZone->hitTest(pt)) {
		if (_selectedUnitBarItemIndex >= 0 && _selectedUnitBarItemIndex < (int)formationInfo.units.size()) {
			formationInfo.units.erase(formationInfo.units.begin() + _selectedUnitBarItemIndex);
			updateUnitBarItems();
		} else {
			_DHLOG("FormationScene::handleAddingUnit: _selectedUnitBarItemIndex out of range");
		}
	} else {
		_draggingTargetGrid = getGridIndexAtPoint(pt);
		bool canPlace = canPlaceUnitHere(_addingUnit, _draggingTargetGrid.globalLineIndex, _draggingTargetGrid.rowIndex);
		if (_draggingTargetGrid.squadIndex != -1 && canPlace) {

			Unit *unit = UnitFactory::createUnit(_addingUnit->unitInfo, 0.6f);
			unit->gridInfo.set(_draggingTargetGrid.globalGridIndex);
			unit->sprite->setZOrder(static_cast<int>(GM->screenSize.height - unit->position.y));
			_layerFormation->addChild(unit->sprite);

			removeTargetGridsSoldier(unit, _draggingTargetGrid.globalLineIndex, _draggingTargetGrid.rowIndex);
			setSoldierPosition(unit);
			updateUnitOccupiedGrids(unit, _draggingTargetGrid.globalLineIndex, _draggingTargetGrid.rowIndex);

			formationInfo.grids[_draggingTargetGrid.globalLineIndex][unit->gridInfo.rowIndex] = unit->unitInfo.name;
			_soldiers[_draggingTargetGrid.globalLineIndex][_draggingTargetGrid.rowIndex] = unit;
		}
	}

	clearGridsState();
}

void FormationScene::handleDroppingSoldier(CCPoint const &pt) {
	_draggingTargetGrid = getGridIndexAtPoint(pt);
	bool canPlaceHere = canPlaceUnitHere(_draggingSoldier, _draggingTargetGrid.globalLineIndex, _draggingTargetGrid.rowIndex);

	if (_draggingTargetGrid.squadIndex != -1 && canPlaceHere) {
		removeTargetGridsSoldier(_draggingSoldier, _draggingTargetGrid.globalLineIndex, _draggingTargetGrid.rowIndex);

		_draggingSoldier->gridInfo = _draggingTargetGrid;
		setSoldierPosition(_draggingSoldier);
		updateUnitOccupiedGrids(_draggingSoldier, _draggingTargetGrid.globalLineIndex, _draggingTargetGrid.rowIndex);

		_draggingSoldier->sprite->getParent()->reorderChild(_draggingSoldier->sprite, static_cast<int>(GM->screenSize.height - _draggingSoldier->position.y));

		_soldiers[_draggingSoldier->gridInfo.globalLineIndex][_draggingSoldier->gridInfo.rowIndex] = _draggingSoldier;
		formationInfo.grids[_draggingSoldier->gridInfo.globalLineIndex][_draggingSoldier->gridInfo.rowIndex] = _draggingSoldier->unitInfo.name;
	} else {
		delete _draggingSoldier;
		_draggingSoldier = nullptr;
	}

	clearGridsState();
}

void FormationScene::ccTouchCancelled(CCTouch *pTouch, CCEvent *pEvent) {
	_touchPlace = FormationSceneTouchPlace::blank;
	_draggingSoldier = nullptr;

	if (_addingUnit) {
		_addingUnit->sprite->setVisible(false);
	}

	_draggingStartGrid.set(-1);
	_draggingTargetGrid.set(-1);
	_selectedUnitBarItemIndex = -1;

	_garbageZone->setVisible(false);
}

void FormationScene::createFormation() {
	memset(_gridsState, 0, TOTAL_LINES * ROWS_PER_LINE);

	for (int i = 0; i < TOTAL_LINES; i++) {
		for (int m = 0; m < ROWS_PER_LINE; m++) {
			CCSprite *sprBrick = MAKE_SPRITE("grid_normal.png");
			sprBrick->setPosition(ccp(LINES_X_POS[i], m * EDIT_GRID_HEIGHT + FORMATION_VIEW_FOOTER_HEIGHT + EDIT_GRID_HEIGHT / 2));
			sprBrick->setScale(1.0f);
			sprBrick->setZOrder(0);
			_layerFormation->addChild(sprBrick);

			_gridsStateIndicator[i][m] = sprBrick;

			if (formationInfo.grids[i][m] == "") {
				continue;
			}

			UnitInfo info = formationInfo.getUnitInfo(formationInfo.grids[i][m]);
			Unit *unit = UnitFactory::createUnit(info, 0.6f);
			unit->gridInfo.set(ROWS_PER_LINE * i + m);
			unit->faction = ArmyFaction::left;
			unit->sprite->setZOrder(static_cast<int>(GM->screenSize.height - unit->position.y));

			setSoldierPosition(unit);

			_layerFormation->addChild(unit->sprite);

			_soldiers[i][m] = unit;
			updateUnitOccupiedGrids(unit, i, m);
		}
	}
}

GridInfo FormationScene::getGridIndexAtPoint(const CCPoint &pt) {
	int squadIndex = -1;
	int lineIndex = -1;
	int rowIndex = -1;

	auto localPt = _layerFormation->convertToNodeSpace(pt);
	for (int i = 0; i < SQUAD_COUNT; i++) {
		if (_squadsBody[i]->hitTest(pt)) {
			squadIndex = i;
			break;
		}
	}

	if (squadIndex != -1) {
		int x = (EDIT_SQUAD_WIDTH + EDIT_SQUAD_SPACE) * (SQUAD_COUNT - squadIndex - 1);
		int y = FORMATION_VIEW_FOOTER_HEIGHT;

		x = static_cast<int>(localPt.x) - x;
		y = static_cast<int>(localPt.y) - y;

		lineIndex = LINES_PER_SQUAD - 1 - x / EDIT_GRID_WIDTH;
		rowIndex = y / EDIT_GRID_HEIGHT;
	}

	GridInfo info;
	info.set(squadIndex, lineIndex, rowIndex);
	return info;
}

void FormationScene::hideUnitBar() {
	_unitBar->runAction(CCMoveTo::create(0.2f, ccp(GM->screenSize.width / 2.0f, 0.0f)));
	_isShownUnitBar = false;
}

void FormationScene::handleMessage() {
	MessageData *msg = PEEK_MSG(MessageTarget::formation_scene);
	if (msg) {
		switch (msg->key) {
			case MessageFlag::new_unit: {
				updateUnitBarItems();
				break;
			}
			default:
				break;
		}

		POP_MSG(MessageTarget::formation_scene);
	}
}

void FormationScene::showUnitDetail() {
	if (_selectedUnitBarItemIndex < 0 || _selectedUnitBarItemIndex >= (int)_arrUnitBarItems.size()) {
		_DHLOG("FormationScene::showUnitDetail(): _selectedUnitBarItemIndex out of range");
		return;
	}

	Unit *unit = _arrUnitBarItems[_selectedUnitBarItemIndex];
	_unitDetailLayer->setAttribute(unit, true);

	CCSize size = _unitDetailLayer->getContentSize();
	CCPoint pos = unit->sprite->getParent()->convertToWorldSpace(unit->position);
	if (pos.x > GM->screenSize.width - size.width * GM->minscale) {
		pos.x = GM->screenSize.width - size.width * GM->minscale;
	}

	pos.y = 125 * GM->minscale;

	_unitDetailLayer->setPosition(pos);
	_unitDetailLayer->setVisible(true);
}

void FormationScene::keyBackClicked() {
	onBack(nullptr);
}

void FormationScene::keyMenuClicked() {

}

void FormationScene::onEnter() {
	CCScene::onEnter();

	CCDirector::sharedDirector()->getTouchDispatcher()->addTargetedDelegate(this, -1, true);
	CCDirector::sharedDirector()->getKeypadDispatcher()->addDelegate(this);
}

void FormationScene::onExit() {
	CCScene::onExit();

	CCDirector::sharedDirector()->getTouchDispatcher()->removeDelegate(this);
	CCDirector::sharedDirector()->getKeypadDispatcher()->removeDelegate(this);
}

void FormationScene::updateUnitOccupiedGrids(Unit *unit, int line, int row) {
	if (unit->creature->genre == EquipmentGenre::artillery_3case) {
		_gridsState[line][row] = unit->id;
		_gridsState[line + 1][row] = unit->id;
		_gridsState[line + 2][row] = unit->id;
	} else if (unit->creature->genre == EquipmentGenre::artillery_6case) {
		_gridsState[line][row] = unit->id;
		_gridsState[line + 1][row] = unit->id;
		_gridsState[line + 2][row] = unit->id;
		_gridsState[line][row + 1] = unit->id;
		_gridsState[line + 1][row + 1] = unit->id;
		_gridsState[line + 2][row + 1] = unit->id;
	} else if (unit->creature->genre == EquipmentGenre::artillery_9case) {
		_gridsState[line][row] = unit->id;
		_gridsState[line + 1][row] = unit->id;
		_gridsState[line + 2][row] = unit->id;
		_gridsState[line][row + 1] = unit->id;
		_gridsState[line + 1][row + 1] = unit->id;
		_gridsState[line + 2][row + 1] = unit->id;
		_gridsState[line][row + 2] = unit->id;
		_gridsState[line + 1][row + 2] = unit->id;
		_gridsState[line + 2][row + 2] = unit->id;
	} else if (unit->special->genre == EquipmentGenre::special_horse || unit->special->genre == EquipmentGenre::special_boar) {
		_gridsState[line][row] = unit->id;
		_gridsState[line + 1][row] = unit->id;
	} else {
		_gridsState[line][row] = unit->id;
	}
}

void FormationScene::updateGridsState(Unit *unit, int line, int row) {
	clearGridsState();

	if (line < 0 || row < 0) {
		return;
	}

	bool canPlaceHere = canPlaceUnitHere(unit, line, row);
	int squadStartLineIndex = line / LINES_PER_SQUAD * LINES_PER_SQUAD;

	const char *s = (canPlaceHere ? "grid_valid.png" : "grid_invalid.png");

	if (unit->creature->genre == EquipmentGenre::artillery_3case) {
		for (int i = line; i < squadStartLineIndex + LINES_PER_SQUAD; ++i) {
			_gridsStateIndicator[i][row]->setDisplayFrame(MAKE_SPRITEFRAME(s));
		}
	} else if (unit->creature->genre == EquipmentGenre::artillery_6case) {
		for (int i = line; i < squadStartLineIndex + LINES_PER_SQUAD; ++i) {
			_gridsStateIndicator[i][row]->setDisplayFrame(MAKE_SPRITEFRAME(s));
			if (row + 1 < ROWS_PER_LINE) {
				_gridsStateIndicator[i][row + 1]->setDisplayFrame(MAKE_SPRITEFRAME(s));
			}
		}
	} else if (unit->creature->genre == EquipmentGenre::artillery_9case) {
		for (int i = line; i < squadStartLineIndex + LINES_PER_SQUAD; ++i) {
			_gridsStateIndicator[i][row]->setDisplayFrame(MAKE_SPRITEFRAME(s));
			if (row + 1 < ROWS_PER_LINE) {
				_gridsStateIndicator[i][row + 1]->setDisplayFrame(MAKE_SPRITEFRAME(s));
			}
			if (row + 2 < ROWS_PER_LINE) {
				_gridsStateIndicator[i][row + 2]->setDisplayFrame(MAKE_SPRITEFRAME(s));
			}
		}
	} else if (unit->special->genre == EquipmentGenre::special_horse || unit->special->genre == EquipmentGenre::special_boar) {
		_gridsStateIndicator[line][row]->setDisplayFrame(MAKE_SPRITEFRAME(s));
		if (line + 1 < squadStartLineIndex + LINES_PER_SQUAD) {
			_gridsStateIndicator[line + 1][row]->setDisplayFrame(MAKE_SPRITEFRAME(s));
		}
	} else {
		_gridsStateIndicator[line][row]->setDisplayFrame(MAKE_SPRITEFRAME(s));
	}
}

void FormationScene::clearGridsState() {
	for (int i = 0; i < TOTAL_LINES; ++i) {
		for (int j = 0; j < ROWS_PER_LINE; ++j) {
			_gridsStateIndicator[i][j]->setDisplayFrame(MAKE_SPRITEFRAME("grid_normal.png"));
		}
	}
}

bool FormationScene::canPlaceUnitHere(Unit *unit, int line, int row) {
	bool ret = true;
	int squadStartLineIndex = line / LINES_PER_SQUAD * LINES_PER_SQUAD;

	if (unit->creature->genre == EquipmentGenre::artillery_3case) {
		if (line != squadStartLineIndex) {
			ret = false;
		}
	} else if (unit->creature->genre == EquipmentGenre::artillery_6case) {
		if (line != squadStartLineIndex || row >= ROWS_PER_LINE - 1) {
			ret = false;
		}
	} else if (unit->creature->genre == EquipmentGenre::artillery_9case) {
		if (line != squadStartLineIndex || row >= ROWS_PER_LINE - 2) {
			ret = false;
		}
	} else if (unit->special->genre == EquipmentGenre::special_horse || unit->special->genre == EquipmentGenre::special_boar) {
		if (line > squadStartLineIndex + 1) {
			ret = false;
		}
	}

	return ret;
}

void FormationScene::removeExistedSoldier(int id) {
	if (id <= 0) {
		return;
	}

	for (int i = 0; i < TOTAL_LINES; ++i) {
		for (int j = 0; j < ROWS_PER_LINE; ++j) {
			if (_gridsState[i][j] == id) {
				_gridsState[i][j] = 0;

				Unit *unit = _soldiers[i][j];
				if (unit && unit->id == id) {
					formationInfo.grids[i][j] = "";

					_soldiers[i][j] = nullptr;
					if (unit != _draggingSoldier) {
						delete unit;
					}
				}
			}
		}
	}
}

void FormationScene::setSoldierPosition(Unit *unit) {
	float x = LINES_X_POS[unit->gridInfo.globalLineIndex];
	float y = unit->gridInfo.rowIndex * EDIT_GRID_HEIGHT + FORMATION_VIEW_FOOTER_HEIGHT + EDIT_SOLDIER_BOTTOM_MARGIN;

	if (unit->creature->genre == EquipmentGenre::artillery_3case) {
		x -= EDIT_GRID_WIDTH;
	} else if (unit->creature->genre == EquipmentGenre::artillery_6case) {
		x -= EDIT_GRID_WIDTH;
		y += EDIT_GRID_HEIGHT / 2.0f;
	} else if (unit->creature->genre == EquipmentGenre::artillery_9case) {
		x -= EDIT_GRID_WIDTH;
		y += EDIT_GRID_HEIGHT;
	} else if (unit->special->genre == EquipmentGenre::special_horse || unit->special->genre == EquipmentGenre::special_boar) {
		x -= EDIT_GRID_WIDTH / 2.0f;
	} else {
	}

	unit->setPosition(ccp(x, y));
}

Unit *FormationScene::getSoldierByID(int id) {
	Unit *ret = nullptr;
	if (id > 0) {
		for (int i = 0; i < TOTAL_LINES; ++i) {
			for (int j = 0; j < ROWS_PER_LINE; ++j) {
				Unit *unit = _soldiers[i][j];
				if (unit && unit->id == id) {
					ret = unit;
					break;
				}
			}
		}
	}

	return ret;
}

void FormationScene::removeTargetGridsSoldier(Unit *unit, int line, int row) {
	if(!unit || line < 0 || row < 0) {
		return;
	}

	vector<int> arr;
	if (unit->creature->genre == EquipmentGenre::artillery_3case) {
		arr.push_back(_gridsState[line][row]);
		arr.push_back(_gridsState[line + 1][row]);
		arr.push_back(_gridsState[line + 2][row]);
	} else if (unit->creature->genre == EquipmentGenre::artillery_6case) {
		arr.push_back(_gridsState[line][row]);
		arr.push_back(_gridsState[line + 1][row]);
		arr.push_back(_gridsState[line + 2][row]);
		arr.push_back(_gridsState[line][row + 1]);
		arr.push_back(_gridsState[line + 1][row + 1]);
		arr.push_back(_gridsState[line + 2][row + 1]);
	} else if (unit->creature->genre == EquipmentGenre::artillery_9case) {
		arr.push_back(_gridsState[line][row]);
		arr.push_back(_gridsState[line + 1][row]);
		arr.push_back(_gridsState[line + 2][row]);
		arr.push_back(_gridsState[line][row + 1]);
		arr.push_back(_gridsState[line + 1][row + 1]);
		arr.push_back(_gridsState[line + 2][row + 1]);
		arr.push_back(_gridsState[line][row + 2]);
		arr.push_back(_gridsState[line + 1][row + 2]);
		arr.push_back(_gridsState[line + 2][row] + 2);
	} else if (unit->special->genre == EquipmentGenre::special_horse || unit->special->genre == EquipmentGenre::special_boar) {
		arr.push_back(_gridsState[line][row]);
		arr.push_back(_gridsState[line + 1][row]);
	} else {
		arr.push_back(_gridsState[line][row]);
	}

	if(arr.size() > 1) {
		sort(arr.begin(), arr.end());
		arr.erase(std::unique(arr.begin(), arr.end()), arr.end());
	}

	for(int id : arr) {
		removeExistedSoldier(id);
	}
}

float FormationScene::getUnitScaleForUnitBar(Unit *unit) {
	float ret = 0.0f;

	if (unit->creature->genre == EquipmentGenre::artillery_3case) {
		ret = 0.45f;
	} else if (unit->creature->genre == EquipmentGenre::artillery_6case) {
		ret = 0.45f;
	} else if (unit->creature->genre == EquipmentGenre::artillery_9case) {
		ret = 0.2f;
	} else if (unit->special->genre == EquipmentGenre::special_horse || unit->special->genre == EquipmentGenre::special_boar) {
		ret = 0.6f;
	} else {
		ret = 0.6f;
	}


	return ret;
}
